"""
缓存服务
提供数据缓存功能，提高系统性能
"""

import time
import json
import logging
from typing import Any, Optional, Dict, Callable, Union
from pathlib import Path
import redis
from src.config.settings import settings
from src.utils.logging import get_logger
from src.utils.decorators import handle_errors, timing
from src.exceptions import ResearchException

logger = get_logger(__name__)


class RedisCacheService:
    """Redis缓存服务类"""
    
    def __init__(self, host: Optional[str] = None, port: Optional[int] = None, db: Optional[int] = None, 
                 password: Optional[str] = None, ttl: Optional[int] = None):
        """
        初始化Redis缓存服务
        
        Args:
            host: Redis主机地址
            port: Redis端口
            db: Redis数据库编号
            password: Redis密码
            ttl: 默认缓存存活时间（秒）
        """
        try:
            self.host = host or settings.REDIS_HOST
            self.port = port or settings.REDIS_PORT
            self.db = db or settings.REDIS_DB
            self.password = password or settings.REDIS_PASSWORD
            self.ttl = ttl or settings.CACHE_DEFAULT_TTL
            
            # 创建Redis连接
            self.redis_client = redis.StrictRedis(
                host=self.host,
                port=self.port,
                db=self.db,
                password=self.password,
                decode_responses=True,
                socket_timeout=5  # 添加超时设置
            )
            
            # 验证连接
            if not self.redis_client.ping():
                raise ValueError("无法连接到Redis服务器")
                
            logger.info(f"Redis缓存服务初始化完成，连接到 {self.host}:{self.port}/{self.db}")
            
        except Exception as e:
            logger.error(f"Redis初始化失败: {e}")
            raise
    
    @timing(log_level="DEBUG")
    def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool:
        """
        设置缓存
        
        Args:
            key: 缓存键
            value: 缓存值
            ttl: 存活时间（秒），None使用默认值
            
        Returns:
            是否设置成功
        """
        try:
            # 将值转换为JSON字符串
            value_str = json.dumps(value, ensure_ascii=False)
            
            # 设置缓存
            self.redis_client.setex(key, ttl or self.ttl, value_str)
            
            logger.debug(f"设置Redis缓存: {key}", extra={"ttl": ttl or self.ttl})
            return True
            
        except Exception as e:
            logger.error(f"设置Redis缓存失败 {key}: {e}")
            return False
    
    @timing(log_level="DEBUG")
    def get(self, key: str) -> Any:
        """
        获取缓存值
        
        Args:
            key: 缓存键
            
        Returns:
            缓存值或None
        """
        try:
            # 获取缓存
            value_str = self.redis_client.get(key)
            
            if value_str is None:
                logger.debug(f"Redis缓存未找到: {key}")
                return None
            
            # 解析JSON字符串
            if isinstance(value_str, str):
                value = json.loads(value_str)
            else:
                value = value_str
            
            logger.debug(f"获取Redis缓存: {key}")
            return value
            
        except Exception as e:
            logger.error(f"获取Redis缓存失败 {key}: {e}")
            return None
    
    @timing(log_level="DEBUG")
    def delete(self, key: str) -> bool:
        """
        删除缓存
        
        Args:
            key: 缓存键
            
        Returns:
            是否删除成功
        """
        try:
            # 删除缓存
            result = self.redis_client.delete(key)
            
            logger.debug(f"删除Redis缓存: {key}")
            # 确保返回布尔值
            return bool(result)
            
        except Exception as e:
            logger.error(f"删除Redis缓存失败 {key}: {e}")
            return False
    
    def clear(self) -> bool:
        """
        清空所有缓存
        
        Returns:
            是否清空成功
        """
        try:
            # 清空Redis数据库
            self.redis_client.flushdb()
            
            logger.info("Redis缓存已清空")
            return True
            
        except Exception as e:
            logger.error(f"清空Redis缓存失败: {e}")
            return False
    
    def get_stats(self) -> Dict[str, Any]:
        """
        获取Redis缓存统计信息
        
        Returns:
            统计信息字典
        """
        try:
            # 获取Redis信息
            info = self.redis_client.info()
            
            # 确保info是字典类型
            if not isinstance(info, dict):
                info = {}
            
            return {
                "host": self.host,
                "port": self.port,
                "db": self.db,
                "connected_clients": info.get("connected_clients") if info else None,
                "used_memory": info.get("used_memory_human") if info else None,
                "keyspace": info.get("keyspace") if info else None,
                "uptime_in_days": info.get("uptime_in_days") if info else None,
                "total_connections_received": info.get("total_connections_received") if info else None,
                "total_commands_processed": info.get("total_commands_processed") if info else None
            }
            
        except Exception as e:
            logger.error(f"获取Redis统计信息失败: {e}")
            return {}


def get_redis_cache_service() -> RedisCacheService:
    """获取Redis缓存服务实例"""
    return RedisCacheService()


class CacheService:
    """缓存服务类"""
    
    def __init__(self, cache_dir: Optional[str] = None, default_ttl: Optional[int] = None):
        """
        初始化缓存服务
        
        Args:
            cache_dir: 缓存目录
            default_ttl: 默认缓存存活时间（秒）
        """
        self.cache_dir = Path(cache_dir or settings.CACHE_DIR)
        self.cache_dir.mkdir(parents=True, exist_ok=True)
        self.default_ttl = default_ttl or settings.CACHE_DEFAULT_TTL
        self.memory_cache: Dict[str, Dict] = {}
        
        # 初始化Redis缓存服务
        try:
            self.redis_cache = get_redis_cache_service()
            logger.info("Redis缓存服务初始化成功")
        except Exception as e:
            self.redis_cache = None
            logger.warning(f"Redis缓存服务初始化失败: {e}")
        
        logger.info(f"缓存服务初始化完成，目录: {cache_dir or settings.CACHE_DIR}")
    
    def _get_cache_key(self, key: str) -> str:
        """生成安全的缓存键"""
        # 替换可能引起文件系统问题的字符
        safe_key = key.replace('/', '_').replace('\\', '_').replace(':', '_')
        return safe_key[:100]  # 限制长度
    
    def _get_cache_file_path(self, key: str) -> Path:
        """获取缓存文件路径"""
        safe_key = self._get_cache_key(key)
        return self.cache_dir / f"{safe_key}.json"
    
    @timing(log_level="DEBUG")
    def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool:
        """
        设置缓存
        
        Args:
            key: 缓存键
            value: 缓存值
            ttl: 存活时间（秒），None使用默认值
            
        Returns:
            是否设置成功
        """
        try:
            cache_data = {
                "value": value,
                "expires_at": time.time() + (ttl or self.default_ttl),
                "created_at": time.time(),
                "key": key
            }
            
            # 内存缓存
            self.memory_cache[key] = cache_data
            
            # Redis缓存（如果可用）
            if self.redis_cache:
                try:
                    self.redis_cache.set(key, value, ttl or self.default_ttl)
                except Exception as e:
                    logger.warning(f"设置Redis缓存失败: {e}")
            
            # 文件缓存
            cache_file = self._get_cache_file_path(key)
            with open(cache_file, 'w', encoding='utf-8') as f:
                json.dump(cache_data, f, ensure_ascii=False, indent=2)
            
            logger.debug(f"设置缓存: {key}", extra={"ttl": ttl or self.default_ttl})
            return True
            
        except Exception as e:
            logger.error(f"设置缓存失败 {key}: {e}")
            return False
    
    @timing(log_level="DEBUG")
    def get(self, key: str, default: Any = None) -> Any:
        """
        获取缓存值
        
        Args:
            key: 缓存键
            default: 缓存不存在时的默认值
            
        Returns:
            缓存值或默认值
        """
        try:
            # 首先检查内存缓存
            if key in self.memory_cache:
                cache_data = self.memory_cache[key]
                if time.time() < cache_data["expires_at"]:
                    return cache_data["value"]
                else:
                    # 内存缓存过期，删除
                    del self.memory_cache[key]
                    logger.debug(f"内存缓存过期: {key}")
            
            # 检查Redis缓存（如果可用）
            if self.redis_cache:
                try:
                    redis_value = self.redis_cache.get(key)
                    if redis_value is not None:
                        # 更新内存缓存
                        cache_data = {
                            "value": redis_value,
                            "expires_at": time.time() + self.default_ttl,
                            "created_at": time.time(),
                            "key": key
                        }
                        self.memory_cache[key] = cache_data
                        logger.debug(f"从Redis缓存获取: {key}")
                        return redis_value
                except Exception as e:
                    logger.warning(f"获取Redis缓存失败: {e}")
            
            # 检查文件缓存
            cache_file = self._get_cache_file_path(key)
            if cache_file.exists():
                try:
                    with open(cache_file, 'r', encoding='utf-8') as f:
                        cache_data = json.load(f)
                    
                    # 检查是否过期
                    if time.time() < cache_data["expires_at"]:
                        # 更新内存缓存和Redis缓存
                        self.memory_cache[key] = cache_data
                        if self.redis_cache:
                            try:
                                self.redis_cache.set(key, cache_data["value"], 
                                                   int(cache_data["expires_at"] - time.time()))
                            except Exception as e:
                                logger.warning(f"更新Redis缓存失败: {e}")
                        
                        logger.debug(f"从文件缓存获取: {key}")
                        return cache_data["value"]
                    else:
                        # 文件缓存过期，删除文件
                        cache_file.unlink()
                        logger.debug(f"文件缓存过期并删除: {key}")
                except Exception as e:
                    logger.error(f"读取文件缓存失败 {key}: {e}")
                    # 删除损坏的缓存文件
                    if cache_file.exists():
                        cache_file.unlink()
            
            logger.debug(f"缓存未找到，使用默认值: {key}")
            return default
            
        except Exception as e:
            logger.error(f"获取缓存失败 {key}: {e}")
            return default
    
    def exists(self, key: str) -> bool:
        """
        检查缓存是否存在且未过期
        
        Args:
            key: 缓存键
            
        Returns:
            是否存在有效缓存
        """
        try:
            # 检查内存缓存
            if key in self.memory_cache:
                return time.time() < self.memory_cache[key]["expires_at"]
            
            # 检查文件缓存
            cache_file = self._get_cache_file_path(key)
            if cache_file.exists():
                with open(cache_file, 'r', encoding='utf-8') as f:
                    cache_data = json.load(f)
                return time.time() < cache_data["expires_at"]
            
            return False
            
        except Exception:
            return False
    
    @timing(log_level="DEBUG")
    def delete(self, key: str) -> bool:
        """
        删除缓存
        
        Args:
            key: 缓存键
            
        Returns:
            是否删除成功
        """
        try:
            # 删除内存缓存
            if key in self.memory_cache:
                del self.memory_cache[key]
            
            # 删除Redis缓存
            if self.redis_cache:
                try:
                    self.redis_cache.delete(key)
                except Exception as e:
                    logger.warning(f"删除Redis缓存失败: {e}")
            
            # 删除文件缓存
            cache_file = self._get_cache_file_path(key)
            if cache_file.exists():
                cache_file.unlink()
            
            logger.debug(f"缓存删除成功: {key}")
            return True
            
        except Exception as e:
            logger.error(f"删除缓存失败 {key}: {e}")
            return False
    
    def clear(self) -> bool:
        """
        清空所有缓存
        
        Returns:
            是否清空成功
        """
        try:
            # 清空内存缓存
            self.memory_cache.clear()
            
            # 清空Redis缓存
            if self.redis_cache:
                try:
                    self.redis_cache.clear()
                except Exception as e:
                    logger.warning(f"清空Redis缓存失败: {e}")
            
            # 清空文件缓存
            if self.cache_dir.exists():
                for cache_file in self.cache_dir.glob("*.json"):
                    try:
                        cache_file.unlink()
                    except Exception as e:
                        logger.warning(f"删除缓存文件失败 {cache_file}: {e}")
            
            logger.info("所有缓存清空成功")
            return True
            
        except Exception as e:
            logger.error(f"清空缓存失败: {e}")
            return False
    
    def cleanup_expired(self) -> int:
        """
        清理过期缓存
        
        Returns:
            清理的缓存数量
        """
        cleaned_count = 0
        current_time = time.time()
        
        # 清理内存缓存
        keys_to_remove = []
        for key, cache_data in self.memory_cache.items():
            if current_time >= cache_data["expires_at"]:
                keys_to_remove.append(key)
        
        for key in keys_to_remove:
            del self.memory_cache[key]
            cleaned_count += 1
        
        # 清理文件缓存
        cache_files = list(self.cache_dir.glob("*.json"))
        for cache_file in cache_files:
            try:
                with open(cache_file, 'r', encoding='utf-8') as f:
                    cache_data = json.load(f)
                
                if current_time >= cache_data["expires_at"]:
                    cache_file.unlink()
                    cleaned_count += 1
                    
            except Exception:
                # 如果文件损坏，也删除
                try:
                    cache_file.unlink()
                    cleaned_count += 1
                except Exception:
                    pass
        
        if cleaned_count > 0:
            logger.info(f"清理过期缓存，共清理 {cleaned_count} 个项目")
        
        return cleaned_count
    
    def health_check(self) -> bool:
        """
        检查缓存服务是否正常工作
        
        Returns:
            缓存服务是否健康
        """
        # 简单检查Redis连接
        if self.redis_cache:
            try:
                return self.redis_cache.get_stats() is not None
            except Exception as e:
                logger.warning(f"Redis健康检查失败: {e}")
                return False
        return True
    
    def get_stats(self) -> Dict[str, Any]:
        """
        获取缓存统计信息
        
        Returns:
            统计信息字典
        """
        stats = {
            "memory_cache_count": len(self.memory_cache),
            "cache_dir": str(self.cache_dir),
            "default_ttl": self.default_ttl
        }
        
        # 获取Redis统计信息
        if self.redis_cache:
            try:
                redis_stats = self.redis_cache.get_stats()
                stats["redis_stats"] = redis_stats
            except Exception as e:
                logger.warning(f"获取Redis统计信息失败: {e}")
        
        return stats
    
    def cached(self, key: str, ttl: Optional[int] = None):
        """
        缓存装饰器
        
        Args:
            key: 缓存键
            ttl: 存活时间（秒）
        """
        def decorator(func: Callable):
            def wrapper(*args, **kwargs):
                # 生成缓存键（包含函数参数）
                cache_key = f"{key}:{str(args)}:{str(kwargs)}"
                
                # 检查缓存
                cached_value = self.get(cache_key)
                if cached_value is not None:
                    logger.debug(f"缓存命中: {cache_key}")
                    return cached_value
                
                # 执行函数并缓存结果
                result = func(*args, **kwargs)
                self.set(cache_key, result, ttl)
                
                logger.debug(f"缓存未命中，设置缓存: {cache_key}")
                return result
            return wrapper
        return decorator
    
    def batch_get(self, keys: list) -> Dict[str, Any]:
        """
        批量获取缓存
        
        Args:
            keys: 缓存键列表
            
        Returns:
            缓存结果字典 {key: value}
        """
        results = {}
        for key in keys:
            value = self.get(key)
            if value is not None:
                results[key] = value
        return results
    
    def batch_set(self, items: Dict[str, Any], ttl: Optional[int] = None) -> int:
        """
        批量设置缓存
        
        Args:
            items: 缓存项字典 {key: value}
            ttl: 存活时间（秒）
            
        Returns:
            成功设置的缓存数量
        """
        success_count = 0
        for key, value in items.items():
            if self.set(key, value, ttl):
                success_count += 1
        return success_count


# 创建全局缓存服务实例
cache_service = CacheService()



